/* 
 * Copyright 2010 Ziya Guven Koylu, Ozge Koylu
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at 
 * 
 * http://www.apache.org/licenses/LICENSE-2.0
 * 
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package com.koylu.caffein;

import java.sql.Connection;
import java.util.Iterator;
import java.util.Vector;

import com.koylu.caffein.model.clazz.Clazz;

public class Transaction {
	
	private Caffein caffein;
	private Vector<Task> tasks;
	private volatile boolean committed = false;
	
	public Transaction(Caffein caffein) {
		this.caffein = caffein;
		tasks = new Vector<Task>();
	}
	
	public Vector<Task> getTasks() {
		return tasks;
	}
	
	public Transaction save(Object object) throws Exception {
		if(!committed){
			tasks.add(new SaveTask(caffein.getClazz(object.getClass().getName()), object));
		}
		return this;
	}
	
	public Transaction update(Object object) throws Exception {
		if(!committed){
			tasks.add(new UpdateTask(caffein.getClazz(object.getClass().getName()), object));
		}
		return this;
	}
	
	public Transaction delete(String type, Object id) throws Exception {
		if(!committed){
			tasks.add(new DeleteTask(caffein.getClazz(type), id));
		}
		return this;
	}
	
	public Transaction delete(Class<?> clazz, Object id) throws Exception {
		if(!committed){
			tasks.add(new DeleteTask(caffein.getClazz(clazz.getName()), id));
		}
		return this;
	}
	
	public synchronized void commit() throws Exception{
		if(!committed){
			committed = true;
			Connection connection = null;
			try {
				connection = caffein.getCaffeinConfig().getConnection();
				connection.setAutoCommit(false);
				for (Iterator<Task> iterator = tasks.iterator(); iterator.hasNext();) {
					iterator.next().commit(caffein, connection);
				}
				connection.commit();
			} catch (Exception e) {
				if (connection != null) {
					connection.rollback();
				}
				throw e;
			} finally {
				if (connection != null) {
					try {
						connection.close();					
					} catch (Exception e) {}
				}
			}			
		}
	}
	
	private static interface Task{
		public void commit(Caffein caffein, Connection connection) throws Exception;
		public Object getObject();
		public Clazz getClazz();
		public boolean getResult();
	}
	
	private static class SaveTask implements Task{
		private Clazz clazz;
		private Object object;
		public SaveTask(Clazz clazz, Object object) {
			this.clazz = clazz;
			this.object = object;
		}
		public void commit(Caffein caffein, Connection connection) throws Exception{
			caffein.save(clazz, connection, object);
		}
		public Object getObject() {
			return object;
		}
		public Clazz getClazz() {
			return clazz;
		}
		public boolean getResult() {
			return true;
		}
	}
	
	private static class UpdateTask implements Task{
		private boolean result;
		private Clazz clazz;
		private Object object;
		public UpdateTask(Clazz clazz, Object object) {
			this.clazz = clazz;
			this.object = object;
		}
		public void commit(Caffein caffein, Connection connection) throws Exception{
			result = caffein.update(clazz, connection, object);
		}
		public Object getObject() {
			return object;
		}
		public Clazz getClazz() {
			return clazz;
		}
		public boolean getResult() {
			return result;
		}
	}
	
	private static class DeleteTask implements Task{
		private boolean result;
		private Clazz clazz;
		private Object object;
		public DeleteTask(Clazz clazz, Object object) {
			this.clazz = clazz;
			this.object = object;
		}
		public void commit(Caffein caffein, Connection connection) throws Exception{
			result = caffein.delete(clazz, connection, object);
		}
		public Object getObject() {
			return object;
		}
		public Clazz getClazz() {
			return clazz;
		}
		public boolean getResult() {
			return result;
		}
	}
}